#include "D3D9Renderer.h"
#include "../Game/Definitions.h"
#include "../Utility/Misc/DebugOutput.h"

#include <dxgi.h>
#include <fstream>

using std::ofstream;
using std::ifstream;
using std::ios_base;

#ifdef _DEBUG
#include "../Utility/Misc/DebugOutput.h"
#include<sstream>
using std::stringstream;
#endif

#include "D3d9types.h"

D3DXMATRIX				D3D9Renderer::matOrtho;
D3DXMATRIX				D3D9Renderer::matIdentity;

D3DPRESENT_PARAMETERS	D3D9Renderer::pp;
IDirect3D9*				D3D9Renderer::d3d;
IDirect3DDevice9*		D3D9Renderer::device;
ID3DXEffectPool*		D3D9Renderer::effectPool;
HWND					D3D9Renderer::hwnd;
IDirect3DVertexBuffer9*	D3D9Renderer::vertexBuffer;
LPD3DXSPRITE			D3D9Renderer::spriteDevice;			


DWORD					D3D9Renderer::D3DFVF_TLVERTEX = D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1;


bool D3D9Renderer::Initialize(HWND _hwnd, bool _fullscreen, bool _vsync, int _horizontalRes, int _verticalRes)
{
	hwnd = _hwnd;

	device = NULL;
	HRESULT result;
	D3DFORMAT format = D3DFMT_A8R8G8B8;

	d3d = Direct3DCreate9(D3D_SDK_VERSION);

	ZeroMemory(&pp, sizeof(D3DPRESENT_PARAMETERS));

	pp.BackBufferCount = 1;
	pp.BackBufferWidth   = _horizontalRes;
	pp.BackBufferHeight  = _verticalRes;
	pp.BackBufferFormat = format;

	pp.MultiSampleType=D3DMULTISAMPLE_NONE;
	pp.MultiSampleQuality=0;

	pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	pp.hDeviceWindow = _hwnd;
	pp.Windowed = !_fullscreen;
	pp.FullScreen_RefreshRateInHz=D3DPRESENT_RATE_DEFAULT;
	pp.PresentationInterval		= (_vsync) ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;

	pp.EnableAutoDepthStencil = true;
	pp.AutoDepthStencilFormat = D3DFMT_D24S8;

	pp.Flags = 0;

	if(!d3d)
	{
#ifdef _DEBUG
		DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::FAILURE_CREATE ,"DIRECT3D9 COM OBJECT");
#endif
		return false;
	}

#ifdef _DEBUG
	DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::SUCCESS_CREATE ,"DIRECT3D9 COM OBJECT");
#endif

	result = d3d->CreateDevice(0, D3DDEVTYPE_HAL, pp.hDeviceWindow, D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED, &pp, &device);
	if(result < 0)
	{
#ifdef _DEBUG
		DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::FAILURE_CREATE ,"DIRECT3D9 DEVICE");
#endif
		return false;
	}

#ifdef _DEBUG
	DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::SUCCESS_CREATE ,"DIRECT3D9 DEVICE");
#endif
	
	result = D3DXCreateEffectPool(&effectPool);
	
	if(result < 0)
	{
#ifdef _DEBUG
		DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::FAILURE_CREATE ,"DIRECT3D9 EFFECT POOL");
#endif
		return false;
	}

#ifdef _DEBUG
	DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::SUCCESS_CREATE ,"DIRECT3D9 EFFECT POOL");
#endif

	result = D3DXCreateSprite(device, &spriteDevice);

	if(result < 0)
	{
#ifdef _DEBUG
		DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::FAILURE_CREATE ,"DIRECT3D SPRITE DEVICE");
#endif
		return false;
	}

#ifdef _DEBUG
	DebugOutput::Print(DebugOutput::RENDER_MSG, DebugOutput::SUCCESS_CREATE ,"DIRECT3D SPRITE DEVICE");
#endif


	//SETTING UP THE VERTEX SHADER TO WORK 2D
	device->SetVertexShader(NULL);
	device->SetFVF(D3DFVF_TLVERTEX);

	//SETTING UP AND CREATING THE VERTEX BUFFER
	device->CreateVertexBuffer(	sizeof(TLVERTEX) * 4,
								NULL,
								D3DFVF_TLVERTEX,
								D3DPOOL_MANAGED,
								&vertexBuffer,
								NULL);
	device->SetStreamSource(0, vertexBuffer, 0, sizeof(TLVERTEX));

	//SETTING THE APPROPRIATE RENDER STATES
	device->SetRenderState(D3DRS_LIGHTING, false);
	device->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
	device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
	device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);


	D3DXMatrixOrthoLH(&matOrtho, (float)_horizontalRes, (float)_verticalRes, 1.0f, 10.0f);
	D3DXMatrixIdentity(&matIdentity);
	device->SetTransform(D3DTS_PROJECTION, &matOrtho);
	device->SetTransform(D3DTS_WORLD, &matIdentity);
	device->SetTransform(D3DTS_VIEW, &matIdentity);

	SetupQuad();

	return true;
}

void D3D9Renderer::SetupQuad()
{
	TLVERTEX* vertices = NULL;
	vertexBuffer->Lock(0, 4 * sizeof(TLVERTEX), (void**)&vertices, 0);

	//Setup vertices
	vertices[0].color = 0xffffffff;
	vertices[0].x = 0.0f;
	vertices[0].y = 0.0f;
	vertices[0].z = 1.0f;
	vertices[0].u = 0.0f;
	vertices[0].v = 0.0f;

	vertices[1].color = 0xffffffff;
	vertices[1].x = 1.0f;
	vertices[1].y = 0.0f;
	vertices[1].z = 1.0f;
	vertices[1].u = 1.0f;
	vertices[1].v = 0.0f;

	vertices[2].color = 0xffffffff;
	vertices[2].x = 1.0f;
	vertices[2].y = -1.0f;
	vertices[2].z = 1.0f;
	vertices[2].u = 1.0f;
	vertices[2].v = 1.0f;

	vertices[3].color = 0xffffffff;
	vertices[3].x = 0.0f;
	vertices[3].y = -1.0f;
	vertices[3].z = 1.0f;
	vertices[3].u = 0.0f;
	vertices[3].v = 1.0f;

	vertexBuffer->Unlock();
}

void D3D9Renderer::Clear(DWORD _count, const D3DRECT* _rectsptr, DWORD _flags, D3DCOLOR _clearColor, float _clearDepth, DWORD _clearStencil)
{
	device->Clear(_count, _rectsptr, _flags, _clearColor, _clearDepth, _clearStencil);
}

void D3D9Renderer::BeginScene()
{
	device->BeginScene();
}

void D3D9Renderer::BeginSprite()
{
	spriteDevice->Begin(D3DXSPRITE_ALPHABLEND);
}

void D3D9Renderer::EndScene()
{
	device->EndScene();
}

void D3D9Renderer::EndSprite()
{
	spriteDevice->End();
}

void D3D9Renderer::Present(const RECT* _sourceRectPtr, const RECT* _destRectPtr, HWND _hDestWindow, const RGNDATA* _dirtyRegionPtr)
{
	device->Present(_sourceRectPtr, _destRectPtr, _hDestWindow, _dirtyRegionPtr);
}

void D3D9Renderer::Shutdown()
{
	device->SetStreamSource(0, NULL, 0, 0);
	ReleaseCOM(spriteDevice);
	ReleaseCOM(vertexBuffer);
	ReleaseCOM(effectPool);
	ReleaseCOM(device);
	ReleaseCOM(d3d);
}

void D3D9Renderer::ResetDevice()
{

}